/*
 *
 *  *    Copyright 2020-2021 luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.samples.webflux.webfluxdemo.controller;

import com.luter.heimdall.core.config.ConfigManager;
import com.luter.heimdall.core.config.HeimdallProperties;
import com.luter.heimdall.core.details.UserDetails;
import com.luter.heimdall.core.exception.HeimdallException;
import com.luter.heimdall.core.exception.HeimdallExcessiveAttemptsException;
import com.luter.heimdall.core.limiter.PasswordRetryLimiter;
import com.luter.heimdall.core.manager.AuthorizationManager;
import com.luter.heimdall.core.token.PageModel;
import com.luter.heimdall.core.token.SimpleToken;
import com.luter.heimdall.core.utils.ObjUtil;
import com.luter.heimdall.samples.webflux.webfluxdemo.dto.SysUserDTO;
import com.luter.heimdall.samples.webflux.webfluxdemo.service.DataUtil;
import com.luter.heimdall.samples.webflux.webfluxdemo.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {

    private final AuthorizationManager authorizationManager;

    private final PasswordRetryLimiter retryLimiter;

    private final SysUserService sysUserService;

    @PostMapping("/login")
    public String login(@RequestBody SysUserDTO user) {
        final SysUserDTO userByName = sysUserService.getUserByName(user.getUsername());
        if (null == userByName) {
            throw new HeimdallException("用户名或者密码错误:NonExisted");
        }
        final HeimdallProperties config = ConfigManager.getConfig();
        //看看是不是已经锁定了，锁定了，就只能等。
        //这里拿 username 作为缓存 key,实际需要确保这个 key 不重复
        final int count = retryLimiter.availableTimes(userByName.getUsername());
        if (count <= 0) {
            throw new HeimdallException("Your account is locked. Please try again after "
                    + config.getLimiter().getLockedDuration() + " Minutes");
        }
        //登录凭据不正确,进入计次
        //比如，密码不匹配
        if (!DataUtil.matches(user.getPassword(), userByName.getPassword())) {
            //还可以重试，那单纯就是密码不对
            if (!retryLimiter.overLimitWhenIncremented(userByName.getUsername())) {
                String text = (count - 1) == 0 ? "别试了,锁你:"
                        + config.getLimiter().getLockedDuration() + "分钟" : "还可尝试:" + (count - 1) + "次";
                throw new HeimdallException("incorrect user name or password." +
                        " ensure both user account and password are valid.|=" + text);
            }
            throw new HeimdallExcessiveAttemptsException();
        }
        ///构造用户详情
        final UserDetails userDetails = new UserDetails(userByName.getApp(), userByName.getId() + "")
                .setAttributes(ObjUtil.ConvertObjectToMap(userByName, "password"));
        final String login = authorizationManager.getAuthenticationManager().login(userDetails);
        //登录成功后,次数清零
        retryLimiter.release(userByName.getUsername());
        return login;
    }

    @GetMapping("/current")
    public SimpleToken user() {
        return authorizationManager.getAuthenticationManager().getCurrentToken(true);
    }

    @PostMapping("/logout")
    public SimpleToken logout() {
        authorizationManager.getAuthenticationManager().getTokenStore().getActiveTokens();
        return authorizationManager.getAuthenticationManager().logout();
    }

    @GetMapping("/users")
    public PageModel<SimpleToken> users(
            @RequestParam(value = "appId", required = false) String appId,
            @RequestParam(value = "page", defaultValue = "1", required = false) int page,
            @RequestParam(value = "size", defaultValue = "10", required = false) int size
    ) {
        return authorizationManager.getAuthenticationManager().getTokenStore().getActiveTokensPage(appId, page, size);
    }
}
